/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package knn;

import Dao.Arquivo;
import Pojo.Knn;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @author magelinha
 */
public class Execucao {

        public static List<Knn> treino = new ArrayList<Knn>();
        public static List<Knn> teste = new ArrayList<Knn>();

        //essa lista será o resultado gerado entre o teste o treino
        public static List<Knn> listaTemporaria = new ArrayList<Knn>();

        public static int k = 5; //valor de k. Pode ser inserido pelo usuário
        public static int totalAcertos = 0;
        public static int totalErros = 0;
        public static double taxaAcertos = 0;
        public static double tempoTreino = 0;
        public static double tempoTeste = 0;
        int qtdTreino = 0;

        String[] vetorBuying = {"low","med","high","vhigh"};
        String[] vetorMaint = {"low","med","high","vhigh"};
        String[] vetorDoors = {"2","3","4","5more"};
        String[] vetorPersons = {"2","4","more"};
        String[] vetorLug_boot = {"small","med","big"};
        String[] vetorSafety = {"low","med","high"};
        String[] vetorClasse = {"unacc","acc","good","vgood"};

        //matriz que servirá para análise da taxa de acerto
        Object[][] analise = {
            {"unacc", 0},
            {"acc", 0},
            {"good", 0},
            {"vgood", 0}
        };

        public static Object[][] matrizConfusao = {
            {"unacc", "unacc", 0},
            {"unacc", "acc", 0}, 
            {"unacc", "good", 0}, 
            {"unacc", "vgood", 0},
            {"acc", "unacc", 0},
            {"acc", "acc", 0},
            {"acc", "good", 0},
            {"acc", "vgood", 0},
            {"good", "unacc", 0},
            {"good", "acc", 0},
            {"good", "good", 0},
            {"good", "vgood", 0},
            {"vgood", "unacc", 0},
            {"vgood", "acc", 0},
            {"vgood", "good", 0},
            {"vgood", "vgood", 0}
        };

        //constantes para facilitar a codificação


        public static final int UNACC_UNACC = 0;
        public static final int UNACC_ACC = 1;
        public static final int UNACC_GOOD = 2;
        public static final int UNACC_VGOOD = 3;
        public static final int ACC_UNACC = 4;
        public static final int ACC_ACC = 5;
        public static final int ACC_GOOD = 6;
        public static final int ACC_VGOOD = 7;
        public static final int GOOD_UNACC = 8;
        public static final int GOOD_ACC = 9;
        public static final int GOOD_GOOD = 10;
        public static final int GOOD_VGOOD = 11;
        public static final int VGOOD_UNACC = 12;
        public static final int VGOOD_ACC = 13;
        public static final int VGOOD_GOOD = 14;
        public static final int VGOOD_VGOOD = 15;

        public static List<Object[]> listaResultado = new ArrayList<Object[]>();


        /**
         * Preenche as listas de treino com 70% da base e a de teste com o restante. Preenche pegando
         * dados aleatórios
         *
         */
        public void preencherListas(){
            int tamanhoLista = Arquivo.listaKnn.size();
            qtdTreino = (int)Math.floor(0.7*tamanhoLista); //70% para treino
            preencherListaTreino(qtdTreino);
            
            for(int i=qtdTreino; i<Arquivo.listaKnn.size(); i++){
                teste.add(Arquivo.listaKnn.get(i));
            }
        }
        
        public void preencherListaTreino(int qtdTreino){
            long inicioTreino = System.currentTimeMillis(); //inicio do treino
            
            treino.clear();
            //preenche a lista de treino
            for(int i=0; i<qtdTreino; i++){
                treino.add(Arquivo.listaKnn.get(i));
            }
            long fimTreino = System.currentTimeMillis(); //inicio do treino
            tempoTreino += (fimTreino - inicioTreino)/1000.;
        }

        /**
         * Calcula a distancia euclidiana
         * @param lista
         * @param knn
         */
        public void distanciaEuclidiana(Knn knn){

            for(Knn item: treino){
                int buying = resultadoSubtracao(knn.getBuying(), item.getBuying(), vetorBuying);
                int maint = resultadoSubtracao(knn.getMaint(), item.getMaint(), vetorMaint);
                int doors = resultadoSubtracao(knn.getDoors(), item.getDoors(), vetorDoors);
                int persons = resultadoSubtracao(knn.getPersons(), item.getPersons(), vetorPersons);
                int lug_boot = resultadoSubtracao(knn.getLug_boot(), item.getLug_boot(), vetorLug_boot);
                int safety = resultadoSubtracao(knn.getSafety(), item.getSafety(), vetorSafety);

                item.setDistancia(Math.sqrt((double)(buying + maint +doors + persons + lug_boot + safety)));
            }
        }

        /**
         * Pesquisa a posição de cada um dos itens no vetor passado como parâmetro
         * e retorno a diferença entre as posições encontradas
         * @param valor1
         * @param valor2
         * @param vetor
         * @return
         */
        public int resultadoSubtracao(String valor1, String valor2, String[] vetor){
            int v1 = 0, v2 = 0;
            //pesquisa a posição o item1 no vetor
            for(int i=0; i<vetor.length; i++){
                if(vetor[i].equals(valor1)){
                    v1 = i;
                    break;
                }
            }

            //pesquisa a posição o item1 no vetor
            for(int i=0; i<vetor.length; i++){
                if(vetor[i].equals(valor2)){
                    v2 = i;
                    break;
                }
            }

            //o resultado é a subtração entre eles
            int resultado = Math.abs(v2-v1);
            //System.out.println("classe1: " + valor1 + "\tclasse2: " + valor2 + "\tv1: " + v1 + 
            //        "\tv2: " + v2 + "\tresultado: " + resultado*resultado + "\tvetor: " + vetor.getClass().getName());
            return resultado*resultado;
        }

        /**
         * Verifica a localização de uma palavra em um determinado vetor
         *
         * @param vetor
         * @param palavra
         * @return
         */
        public int pesquisarNoVetor(String[] vetor, String palavra){
            for(int i=0; i<vetor.length; i++){
                if(vetor[i].equals(palavra)) return i;
            }

            System.out.println("teve erro");
            return -1; //se chegar aqui é porque tem erro
        }

        /**
         * O treino é feito da seguinte forma:<br />
         * Pega-se uma instância da listaTeste faz a distância euclidina com todas do treino. <br />
         *
         * No fim, gerará uma lista de tamanho qtdTreino*qtdTeste.
         *
         */
        public void executarTreino(){
            //para cada item da lista treino
            for(Knn item: teste){
                preencherListaTreino(qtdTreino);
                //calcula a distancia euclidiana
                distanciaEuclidiana(item);

                //ordena a lista treino pelo atributo "distancia";
                Collections.sort(treino, new Comparator<Knn>() {

                    @Override
                    public int compare(Knn t, Knn t1) {
                        BigDecimal b1 = new BigDecimal(t.getDistancia()).setScale(1, RoundingMode.HALF_EVEN);
                        BigDecimal b2 = new BigDecimal(t1.getDistancia()).setScale(1, RoundingMode.HALF_EVEN);
                        
                        if(b1.compareTo(b2)> 0) return 1;
                        else if(b1.compareTo(b2) == 0) return 0;
                        else return -1;

                    }
                });
                
                //depois de ordenada, pega-se os K primeiros itens
                listaTemporaria.clear(); //limpa a lista de resultado, ela será usada a todo momento
                for(int i=0; i<k; i++){
                    listaTemporaria.add(treino.get(i));
                }
                
                long inicioTeste = System.currentTimeMillis();//inicio do teste
                
                //verifica a taxa de ocorrência de cada um
                zerarMatrizAnalise();
                for(Knn aux: listaTemporaria){
                    int pesquisa = pesquisarNoVetor(vetorClasse, aux.getClasse());
                    int valor = Integer.valueOf(analise[pesquisa][1].toString());

                    analise[pesquisa][1] = valor+1;
                }
                
                //percorre o vetor analise buscando a posição de maior ocorrência
                int maior = 0;
                int indiceMaior = 0;
                for(int i=0; i<analise.length; i++){
                    if(Integer.valueOf(analise[i][1].toString()) > maior){
                        maior = Integer.valueOf(analise[i][1].toString());
                        indiceMaior = i;
                    }
                }

                //determina se for erro ou acerto;
                boolean acerto = false;
                if(item.getClasse().equals(analise[indiceMaior][0].toString()))acerto = true;
                
                //joga pra matriz de confusão
                int posicao = definirPosicaoMatrizConfusao(item.getClasse(), analise[indiceMaior][0].toString());
                matrizConfusao[posicao][2] = Integer.valueOf(matrizConfusao[posicao][2].toString()) + 1;
                
                Object[] obj = {item,acerto};
                listaResultado.add(obj);
                long fimTeste = System.currentTimeMillis();
                
                tempoTeste += (fimTeste-inicioTeste)/1000.0; //tempo em segundos
            }

            definirTaxaAcerto();
        }
        
        public void definirTaxaAcerto(){
            totalAcertos = 0;

            for(Object[] item: listaResultado){
                if(Boolean.valueOf(item[1].toString())) {
                    totalAcertos++;
                }
            }

            totalErros = listaResultado.size() - totalAcertos;
            taxaAcertos = ((double)totalAcertos/(double)listaResultado.size())*100;
        }

        public void zerarMatrizAnalise(){
            for(int i=0; i<analise.length; i++){
                analise[i][1] = 0;
            }
        }

        public int definirPosicaoMatrizConfusao(String classe1, String classe2){
            if(classe1.equals("unacc")){
                if(classe2.equals("unacc")) return UNACC_UNACC;
                else if(classe2.equals("acc")) return UNACC_ACC;
                if(classe2.equals("good")) return UNACC_GOOD;
                else return UNACC_VGOOD;
            }if(classe1.equals("acc")){
                if(classe2.equals("unacc")) return ACC_UNACC;
                else if(classe2.equals("acc")) return ACC_ACC;
                if(classe2.equals("good")) return ACC_GOOD;
                else return ACC_VGOOD;
            }if(classe1.equals("good")){
                if(classe2.equals("unacc")) return GOOD_UNACC;
                else if(classe2.equals("acc")) return GOOD_ACC;
                if(classe2.equals("good")) return GOOD_GOOD;
                else return GOOD_VGOOD;
            }else{
                if(classe2.equals("unacc")) return VGOOD_UNACC;
                else if(classe2.equals("acc")) return VGOOD_ACC;
                if(classe2.equals("good")) return VGOOD_GOOD;
                else return VGOOD_VGOOD;
            }


        }
}
